﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2013  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Ext.h".
//-------------------------------------------------------------------

#include "GLESStateManager.h"

namespace UX_STL
{
namespace GLESshell
{
	
/*
=================================================
	конструктор
=================================================
*/
	GLESStateManager::GLESStateManager():
		/*Texture*/			_uCurLayer(0),		_aTexLayers(),
		/*Program*/			_uProgram(0),		_uProgramPipeline(0),
		/*Buffers*/			_uVAO(0),			_uFBO(0),
							_uRBO(0),			_uIBO(0)
	{
	}

/*
=================================================
	деструктор
=================================================
*/
	GLESStateManager::~GLESStateManager()
	{
		_aTexLayers.Clear();
	}
	
/*
=================================================
	инициализация
=================================================
*/
	bool GLESStateManager::_Init()
	{
		_sTexStack.SetDefValue( GLESTexLayer() );
		return true;
	}

/*
=================================================
	сбросить состояния
=================================================
*/
	void GLESStateManager::Refresh()
	{
		for (usize i = 0; i < _aTexLayers.Count(); ++i)
		{
			glActiveTexture( GL_TEXTURE0 + i );
			glBindTexture( GL_TEXTURE_2D, 0 );

			_aTexLayers[i].eTarget	= gl_texture::TEXTURE_2D;
			_aTexLayers[i].uID		= 0;
		}

		_sTexStack.Clear();
		_sVAOStack.Clear();

		_sViewportStack.Clear();
		glActiveTexture( GL_TEXTURE0 );

		glUseProgram( 0 );
		//glBindProgramPipeline( 0 );

		//glBindVertexArray( 0 );
		glBindFramebuffer( GL_FRAMEBUFFER, 0 );
		glBindBuffer( gl_buffer::INDEX,  0 );
		glBindBuffer( gl_buffer::VERTEX, 0 );

		glBlendEquation( GL_FUNC_ADD );
		glBlendFunc( GL_ONE, GL_ZERO );
		glDepthFunc( GL_LEQUAL );
		glDepthMask( GL_TRUE );
		glDisable( GL_BLEND );

		glStencilFunc( GL_ALWAYS, 0, 1 );
		glStencilMask( 0xFFFF );

		glCullFace( GL_BACK );
		glFrontFace( GL_CCW );
		glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
		
		_uCurLayer			= 0;

		_uProgram			= 0;
		_uProgramPipeline	= 0;
		
		_uIBO				= 0;
		_uVAO				= 0;
		_uFBO				= 0;
		_uRBO				= 0;

		_sStates			= GLESRenderState();

		OPENGL_CHECK_ERRORS();
	}
	
/*
=================================================
	отключает все текстуры
=================================================
*/
	void GLESStateManager::UnbindTextures()
	{
		for (isize i = _aTexLayers.Count()-1; i >= 0; --i)
		{
			glActiveTexture( GL_TEXTURE0 + i );
			glBindTexture( GL_TEXTURE_2D, 0 );
			_aTexLayers[i].eTarget	= gl_texture::_UNKNOWN;
			_aTexLayers[i].uID		= 0;
			_uCurLayer				= i;
		}
	}
	
/*
=================================================
	устанавливает состояния рендера
=================================================
*/
	void GLESStateManager::SetRenderState(const GLESRenderState &rs)
	{
		SetColorState( rs.sColorBuffer );
		SetDepthState( rs.sDepth );
		SetStencilState( rs.sStencil );
		SetCullState( rs.sCull );
		SetStates( rs.sStates );
		GL_CALL( glPolygonOffset( rs.vPolygonOffset.x, rs.vPolygonOffset.y ) );
	}


	void GLESStateManager::SetColorState(const GLESRenderState::TColorBuffer &color)
	{
		ColorMask( color.eMask );
		BlendState( color.bBlending );

		if ( color.bBlending )
		{
			BlendFuncSeparate( color.eSrcRGB, color.eDstRGB, color.eSrcAlpha, color.eDstAlpha );
			BlendEquationSeparate( color.eModeRGB, color.eModeAlpha );
		}
	}


	void GLESStateManager::SetDepthState(const GLESRenderState::TDepthState &depth)
	{
		DepthFunc( depth.eFunc );
		DepthMask( depth.bWrite );
		DepthTest( depth.bTest );
	}


	void GLESStateManager::SetStencilState(const GLESRenderState::TStencilState &stencil)
	{
		StencilTest( stencil.bTest );

		StencilMaskSeparate( gl_face::FRONT, stencil.aSide[0].uMask );
		StencilMaskSeparate( gl_face::BACK,  stencil.aSide[1].uMask );

		StencilOpSeparate( gl_face::FRONT, stencil.aSide[0].eSFail, stencil.aSide[0].eDFail, stencil.aSide[0].eDPPass );
		StencilOpSeparate( gl_face::BACK,  stencil.aSide[1].eSFail, stencil.aSide[1].eDFail, stencil.aSide[1].eDPPass );

		StencilFuncSeparate( gl_face::FRONT, stencil.aSide[0].eFunc, stencil.aSide[0].uFuncRef, stencil.aSide[0].uFuncMask );
		StencilFuncSeparate( gl_face::BACK,  stencil.aSide[1].eFunc, stencil.aSide[1].uFuncRef, stencil.aSide[1].uFuncMask );
	}


	void GLESStateManager::SetCullState(const GLESRenderState::TCullState &cull)
	{
		CullFace( cull.eCullFace );
		CullFaceState( cull.bCullFace );
		FrontFace( cull.eFrontFace );
	}


	void GLESStateManager::SetStates(const GLESRenderState::TStates &states)
	{
		GL_CALL( states.bAlphaToCoverage ?
					glEnable( GL_SAMPLE_ALPHA_TO_COVERAGE ) :
					glDisable( GL_SAMPLE_ALPHA_TO_COVERAGE ) );

		GL_CALL( states.bDither ?
					glEnable( GL_DITHER ) :
					glDisable( GL_DITHER ) );

		GL_CALL( states.bPolygonOffset ?
					glEnable( GL_POLYGON_OFFSET_FILL ) :
					glDisable( GL_POLYGON_OFFSET_FILL ) );

		GL_CALL( states.bSampleCoverage ?
					glEnable( GL_SAMPLE_COVERAGE ) :
					glDisable( GL_SAMPLE_COVERAGE ) );

		GL_CALL( glSampleCoverage( states.fSampleCoverage, states.bInvertSampleCoverage ) );
	}
	

	void GLESStateManager::SetColorStateS(const GLESRenderState::TColorBuffer &color)
	{
		ColorMask( color.eMask );
		BlendState( color.bBlending );

		if ( color.bBlending )
		{
			BlendFunc( color.eSrcRGB, color.eDstRGB );
			BlendEquation( color.eModeRGB );
		}
	}


	void GLESStateManager::SetStencilStateS(const GLESRenderState::TStencilState::TSide &stencil, bool bEnable)
	{
		StencilTest( bEnable );
		StencilMask( stencil.uMask );
		StencilOp( stencil.eSFail, stencil.eDFail, stencil.eDPPass );
		StencilFunc( stencil.eFunc, stencil.uFuncRef, stencil.uFuncMask );
	}


	void GLESStateManager::SetPolygonOffset(const vec2 &vOffsets, bool bEnable)
	{
		GL_CALL( glPolygonOffset( vOffsets.x, vOffsets.y ) );
		GL_CALL( bEnable ?	glEnable( GL_POLYGON_OFFSET_FILL ) :
								glDisable( GL_POLYGON_OFFSET_FILL ) );
	}

//-------------------------------------------------------------------

}	// GLESshell
}	// UX_STL
